package source.javadog.mysource;

import source.javadog.util.MyObjectPool;

import java.io.Serializable;
import java.nio.ByteBuffer;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * Author : manyu
 * Date: 2018/9/9 18:35
 * Description:
 */
public abstract class MyDataSqlSource<DBChannel> implements MyDataSource{

    protected final Logger logger = Logger.getLogger(this.getClass().getSimpleName());

    protected int threads;

    protected ThreadPoolExecutor executor;

    protected MyObjectPool<ByteBuffer> bufferPool;

    protected MyPoolSource<DBChannel> pool;

    MyDataSqlSource(Map<String, String> dbMap){
        final AtomicInteger counter = new AtomicInteger();
        this.threads = Integer.decode("" + Runtime.getRuntime().availableProcessors() * 16);
        int maxconns = Math.max(8, Integer.decode("" + Math.min(1000, Runtime.getRuntime().availableProcessors() * 200)));
        final String cname = this.getClass().getSimpleName();
        final Thread.UncaughtExceptionHandler ueh = (t, e) -> {
            logger.log(Level.SEVERE, cname + " error", e);
        };
        this.executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(threads, (Runnable r) -> {
            Thread t = new Thread(r);
            t.setDaemon(true);
            String s = "" + counter.incrementAndGet();
            if (s.length() == 1) {
                s = "00" + s;
            } else if (s.length() == 2) {
                s = "0" + s;
            }
            t.setName(cname + "-Thread-" + s);
            t.setUncaughtExceptionHandler(ueh);
            return t;
        });
        final int bufferCapacity = Math.max(8 * 1024, Integer.decode("" + 8 * 1024));
        this.bufferPool = new MyObjectPool<ByteBuffer>(new AtomicLong(), new AtomicLong(), Math.max(maxconns, this.threads * 2),
                (Object... params) -> ByteBuffer.allocateDirect(bufferCapacity), null, (e) -> {
            if (e == null || e.isReadOnly() || e.capacity() != bufferCapacity) return false;
            e.clear();
            return true;
        });
        ArrayBlockingQueue<DBChannel> queue = maxconns > 0 ? new ArrayBlockingQueue(maxconns) : null;
        Semaphore semaphore = maxconns > 0 ? new Semaphore(maxconns) : null;
        this.pool = createPoolSource(queue, semaphore, dbMap);
    }

    protected abstract MyPoolSource<DBChannel> createPoolSource(ArrayBlockingQueue<DBChannel> queue, Semaphore semaphore, Map<String, String> dbMap);

    //查询单条记录
    protected abstract <T> CompletableFuture<T> findDB(final MyEntityInfo<T> info, final String sql);
    //插入纪录
    protected abstract <T> CompletableFuture<Boolean> insertDB(final MyEntityInfo<T> info, T value);
    //查询列表
    protected abstract <T> CompletableFuture<MySheet<T>> querySheetDB(final MyEntityInfo<T> info);
    //删除记录
    protected abstract <T> CompletableFuture<Boolean> deleteDB(final MyEntityInfo<T> info, final String sql);
    //更新记录
    protected abstract <T> CompletableFuture<Boolean> updateDB(MyEntityInfo<T> info, T update);

    @Override
    public String getType() {
        return "sql";
    }

    public MyEntityInfo apply(Class t) {
        return loadEntityInfo(t);
    }

    protected <T> MyEntityInfo<T> loadEntityInfo(Class<T> clazz) {
        return MyEntityInfo.load(clazz, this.pool == null ? null : this.pool.dbMap, this);
    }

    @Override
    public <T> List<T> findList(Class<T> clazz) {
        final MyEntityInfo<T> info = loadEntityInfo(clazz);
        return querySheetDB(info).join().list(true);
    }

    @Override
    public <T> T find(final Class<T> clazz, final Serializable pk){
        final MyEntityInfo<T> info = loadEntityInfo(clazz);
        final String sql = "SELECT " + "*" + " FROM " + info.getTable() + " WHERE " + info.getPrimarySQLColumn(null) + " = " + pk;
        //todo 加入日志支持
        System.out.println(info.getType().getSimpleName() + " find sql = "  + sql);
        return findDB(info,sql).join();
    }

    @Override
    public <T> boolean save(T insert){
        Class<T> insertClass = (Class<T>) insert.getClass();
        final MyEntityInfo<T> info = loadEntityInfo(insertClass);
        //todo 主键是uuid类型的
        return insertDB(info,insert).join();
    }

    @Override
    public <T> boolean delete(final Class<T> clazz, final Serializable pk){
        final MyEntityInfo<T> info = loadEntityInfo(clazz);
        final String sql = "DELETE FROM " + info.getTable() + " WHERE " + info.getPrimarySQLColumn(null) + " = " + pk;
        //todo 加入日志支持
        System.out.println(info.getType().getSimpleName() + " delete sql = "  + sql);
        return deleteDB(info,sql).join();
    }

    protected <T> T infoGetValue(MyEntityInfo<T> info, final ResultSet set) throws SQLException {
        return info.getValue(set);
    }

    @Override
    public <T>  boolean update(T update){
        Class<T> insertClass = (Class<T>) update.getClass();
        final MyEntityInfo<T> info = loadEntityInfo(insertClass);
        return updateDB(info,update).join();
    }

}
